package com.nulldev.util.VariableAPI.util.random;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;

import com.nulldev.util.VariableAPI.util.random.fasterrng.FasterRandomAdapter;
import com.nulldev.util.VariableAPI.util.random.fasterrng.MRG32k3a;

public class GlobalRandom {
	public static final Random GLOBAL_FALLBACK_RANDOM = new FasterRandomAdapter(new MRG32k3a()) {
		private static final long serialVersionUID = 1623684293218093659L;

		public synchronized void setSeed(long seed) {
		};
	};

	private static byte[] __bytes(final int count) {
		if (count <= 0)
			throw new UnsupportedOperationException("bytes <= 0!");
		final byte[] bytes = new byte[count];
		GLOBAL_FALLBACK_RANDOM.nextBytes(bytes);
		return bytes;
	}

	private static SecureRandom __init_secure_rng() {
		SecureRandom sr;
		try {
			final String algo = RandomAccelMethod.getSecureRandomAlgorithm();
			if (algo == null) {
				sr = new SecureRandom();
			} else {
				sr = SecureRandom.getInstance(algo);
			}
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}
		sr.setSeed(__bytes(64));
		return sr;
	}

	private static final SecureRandom GLOBAL_SECURE_RANDOM = __init_secure_rng();

	private static final SecureRandom GLOBAL_INSECURE_RANDOM = new nSecureRandom(__bytes(8), true) {
		private static final long serialVersionUID = 5644303642938056690L;
		public synchronized void setSeed(byte[] seed) {};
		public void setSeed(long seed) {};
	};

	protected static long raw_nextLong(final Random r, final long min, final long max) {
		if (min > max)
			throw new IllegalArgumentException("min>max");
		if (min == max)
			return min;
		long n = r.nextLong();
		n = n == Long.MIN_VALUE ? 0 : n < 0 ? -n : n;
		n = n % (max - min);
		return min + n;
	}

	public static Random getFallbackRandom() {
		return GLOBAL_FALLBACK_RANDOM;
	}

	public static SecureRandom getGlobalSecureRandom() {
		return GLOBAL_SECURE_RANDOM;
	}

	private static final boolean __USE_RDRAND_OVERRIDE = RandomAccelMethod.hasRDRAND();
	public static SecureRandom getGlobalInsecureRandom() {
		if (__USE_RDRAND_OVERRIDE) {
			return GLOBAL_SECURE_RANDOM;
		} else {
			return GLOBAL_INSECURE_RANDOM;
		}
	}

	public static SecureRandom makeSecureRandom() {
		return new SecureRandom(__bytes(64));
	}
}
